// Lexical parser module

// import Stream module
import Stream = require("./ParserStreams");

// import localization
import Loc = require("../../UI/Localization");

// enumeration symbol type
export enum eSymbolType
{
    STEP, TURNLEFT, PUT, PICK, REPEAT, IF, WHILE, IS, ISNOT,
    MARK, WALL, NORTH, SOUTH, WEST, EAST, ELSE, END, CommandName, RepeatCount, EOF,
    operator, query, condition, command, commandblock, commandblock2, epsilon, elseblock, usercommand
};

// syntax and lexer symbol
export class Symbol {
    public SymbolType: eSymbolType;
    public NumberValue: number;
    public StringValue: string;
}

// lexical symbol and token pair
class LexerPair {
    public Token: string;
    public Symbol: eSymbolType;

    public constructor(token: string, symbol: eSymbolType) {
        this.Token = token;
        this.Symbol = symbol;
    }
}

// lexical stream
export class LexicalStream extends Stream.ParserStream<Symbol> {
}

// the lexical parser itself
export class LexicalParser {

    // lexer map
    private mLexerMap: Array<LexerPair>;
    
    // the input stream
    private mStream: Stream.TokenStream;

    // output parsed symbols
    private mSymbols: Array<Symbol>;

    // localization module
    private mLocalization: Loc.Localization;

    // constructor
    public constructor(stream: Stream.TokenStream) {
        this.mStream = stream;
        this.mSymbols = new Array<Symbol>();
        this.mLocalization = Loc.Localization.Instance;
        this.PrepareLexerMap();
    }

    // property getter to return symbols
    public get Symbols() {
        return this.mSymbols;
    }

    // the main API method
    public Parse(): void {
        var symbol: Symbol = null;
        // iterate all tokens
        while (this.mStream.EOF == false) {
            var token = this.mStream.Get();

            // check the keyword
            var symbolType = this.LookUpSymbol(token);
            if (symbolType != null) {
                symbol = new Symbol();
                symbol.SymbolType = symbolType;
                this.mSymbols.push(symbol);
            }
            else {
                // check repetition count
                var repCount = this.IsRepeatCount(token);
                if (repCount != null) {
                    symbol = new Symbol();
                    symbol.SymbolType = eSymbolType.RepeatCount;
                    symbol.NumberValue = repCount;
                    this.mSymbols.push(symbol);
                }
                else {
                    symbol = new Symbol();
                    symbol.SymbolType = eSymbolType.CommandName;
                    symbol.StringValue = token;
                    this.mSymbols.push(symbol);
                }
            }
        }
    }

    // check the repeat count
    private IsRepeatCount(token: string): number {
        if (token[token.length - 1].toLocaleLowerCase() == "x") {
            // removes "times" symbol
            token = token.substr(0, token.length - 1);

            // parse the number
            var repeatCount = parseInt(token);
            if (isNaN(repeatCount))
                return null;
            else
                return repeatCount;
        }

        return null;
    }

    // looks up symbol type in the map
    private LookUpSymbol(token: string): eSymbolType {
        token = token.toLocaleLowerCase();
        for (var i = 0; i < this.mLexerMap.length; i++) {
            var pair = this.mLexerMap[i];
            if (pair.Token == token)
                return pair.Symbol;
        }

        return null;
    }

    // prepares the lexer map
    private PrepareLexerMap(): void {
        this.mLexerMap = new Array<LexerPair>();
        this.mLexerMap.push(new LexerPair(this.GetCmd("cmdStep"), eSymbolType.STEP));
        this.mLexerMap.push(new LexerPair(this.GetCmd("cmdTurnLeft"), eSymbolType.TURNLEFT));
        this.mLexerMap.push(new LexerPair(this.GetCmd("cmdPut"), eSymbolType.PUT));
        this.mLexerMap.push(new LexerPair(this.GetCmd("cmdPick"), eSymbolType.PICK));
        this.mLexerMap.push(new LexerPair(this.GetCmd("cmdRepeat"), eSymbolType.REPEAT));
        this.mLexerMap.push(new LexerPair(this.GetCmd("cmdIf"), eSymbolType.IF));
        this.mLexerMap.push(new LexerPair(this.GetCmd("cmdWhile"), eSymbolType.WHILE));
        this.mLexerMap.push(new LexerPair(this.GetCmd("cmdIs"), eSymbolType.IS));
        this.mLexerMap.push(new LexerPair(this.GetCmd("cmdIsNot"), eSymbolType.ISNOT));
        this.mLexerMap.push(new LexerPair(this.GetCmd("cmdMark"), eSymbolType.MARK));
        this.mLexerMap.push(new LexerPair(this.GetCmd("cmdWall"), eSymbolType.WALL));
        this.mLexerMap.push(new LexerPair(this.GetCmd("cmdNorth"), eSymbolType.NORTH));
        this.mLexerMap.push(new LexerPair(this.GetCmd("cmdSouth"), eSymbolType.SOUTH));
        this.mLexerMap.push(new LexerPair(this.GetCmd("cmdWest"), eSymbolType.WEST));
        this.mLexerMap.push(new LexerPair(this.GetCmd("cmdEast"), eSymbolType.EAST));
        this.mLexerMap.push(new LexerPair(this.GetCmd("cmdElse"), eSymbolType.ELSE));
        this.mLexerMap.push(new LexerPair(this.GetCmd("cmdEnd"), eSymbolType.END));
    }

    // returns the localized command
    private GetCmd(key: string): string {
        return this.mLocalization.Get(key);
    }
}
